from app.db import db
from app.Models.Goal.GoalCateModel import GoalCate  # 导入分类模型
from app.Models.AdminModels.UserModels.UserModel import User  # 导入用户模型
from sqlalchemy import or_
from datetime import datetime


class GoalCateDao:
    """目标自定义一级分类 DAO 层，封装增删改查操作（含外键关联查询）"""

    @staticmethod
    def add_goal_cate(cate_data):
        """
        新增自定义一级分类
        :param cate_data: 分类数据字典，需包含：first_cate_name；可选：user_id（未传则为 None）
        :return: 新增的分类对象（含 id）
        """
        try:
            # 构建分类对象
            new_cate = GoalCate(
                user_id=cate_data.get("user_id"),  # 支持未分配用户（公共分类）
                first_cate_name=cate_data["first_cate_name"]
            )
            # 提交到数据库（注意：first_cate_name 有 unique 约束，重复会抛异常）
            db.session.add(new_cate)
            db.session.commit()
            return new_cate
        except Exception as e:
            db.session.rollback()
            # 处理分类名称重复的异常提示（适配 SQLAlchemy 异常）
            if "Duplicate entry" in str(e) and "first_cate_name" in str(e):
                raise Exception(f"新增分类失败：分类名称「{cate_data['first_cate_name']}」已存在（名称需唯一）")
            raise Exception(f"新增分类失败：{str(e)}")

    @staticmethod
    def delete_goal_cate(cate_id):
        """
        根据 ID 删除自定义一级分类
        :param cate_id: 分类 ID
        :return: bool：删除成功返回 True，失败返回 False
        """
        try:
            cate = GoalCate.query.get(cate_id)
            if not cate:
                raise Exception(f"分类 ID {cate_id} 不存在")
            # 执行删除
            db.session.delete(cate)
            db.session.commit()
            return True
        except Exception as e:
            db.session.rollback()
            # 处理外键关联删除限制（如果分类已被目标表引用）
            if "foreign key constraint fails" in str(e).lower():
                raise Exception(f"删除分类失败：该分类已关联目标数据，无法直接删除")
            raise Exception(f"删除分类失败：{str(e)}")

    @staticmethod
    def update_goal_cate(cate_id, update_data):
        """
        根据 ID 更新自定义一级分类
        :param cate_id: 分类 ID
        :param update_data: 待更新数据字典（可选字段：user_id、first_cate_name）
        :return: 更新后的分类对象
        """
        try:
            cate = GoalCate.query.get(cate_id)
            if not cate:
                raise Exception(f"分类 ID {cate_id} 不存在")

            # 批量更新字段（排除 id，仅更新传入的有效字段）
            valid_fields = ["user_id", "first_cate_name"]
            for key, value in update_data.items():
                if key in valid_fields and hasattr(cate, key):
                    setattr(cate, key, value)

            # 提交更新（注意名称唯一约束）
            db.session.commit()
            return cate
        except Exception as e:
            db.session.rollback()
            # 处理分类名称重复的异常提示
            if "Duplicate entry" in str(e) and "first_cate_name" in str(e):
                raise Exception(f"更新分类失败：分类名称「{update_data.get('first_cate_name')}」已存在（名称需唯一）")
            raise Exception(f"更新分类失败：{str(e)}")

    @staticmethod
    def get_goal_cate_by_id(cate_id):
        """
        根据 ID 查询分类（关联查询所属用户名）
        :param cate_id: 分类 ID
        :return: 字典：包含分类所有字段 + username（用户名）
        """
        try:
            # 关联查询：GoalCate 左关联 User（外键 user_id）
            query_result = db.session.query(
                GoalCate,
                User.username  # 关联查询用户名
            ).join(
                User,
                GoalCate.user_id == User.id,
                isouter=True  # 左关联：允许 user_id 为 None（公共分类）
            ).filter(
                GoalCate.id == cate_id
            ).first()

            if not query_result:
                return None  # 分类不存在

            cate, username = query_result
            # 格式化返回结果
            return {
                "id": cate.id,
                "user_id": cate.user_id,
                "username": username or "公共分类",  # 无用户时显示「公共分类」
                "first_cate_name": cate.first_cate_name,
                "create_time": cate.create_time.strftime("%Y-%m-%d %H:%M:%S") if hasattr(cate, 'create_time') else None
                # 若有创建时间字段则添加
            }
        except Exception as e:
            raise Exception(f"查询分类失败：{str(e)}")

    @staticmethod
    def query_goal_cates(filters=None, page=1, per_page=30):
        """
        分页查询分类列表（支持多条件过滤，关联查询用户名）
        :param filters: 过滤条件字典（可选），支持：
            - user_id: 用户 ID（精确匹配）
            - username: 用户名（模糊匹配，关联 User）
            - first_cate_name: 分类名称（模糊匹配）
            - is_public: 是否公共分类（bool，True 表示 user_id 为 None）
        :param page: 页码（默认第 1 页）
        :param per_page: 每页条数（默认 10 条）
        :return: 分页对象：包含 items（分类列表）、total（总条数）、page（当前页）、pages（总页数）
        """
        try:
            # 基础查询：关联用户表
            query = db.session.query(
                GoalCate,
                User.username
            ).join(
                User,
                GoalCate.user_id == User.id,
                isouter=True
            )

            # 处理过滤条件
            if filters and isinstance(filters, dict):
                # 按用户 ID 过滤
                if "user_id" in filters:
                    query = query.filter(GoalCate.user_id == filters["user_id"])
                # 按用户名模糊过滤（关联表字段）
                if "username" in filters and filters["username"]:
                    query = query.filter(User.username.like(f"%{filters['username']}%"))
                # 按分类名称模糊过滤
                if "first_cate_name" in filters and filters["first_cate_name"]:
                    query = query.filter(GoalCate.first_cate_name.like(f"%{filters['first_cate_name']}%"))
                # 按是否公共分类过滤（is_public=True 表示 user_id 为 None）
                if "is_public" in filters and isinstance(filters["is_public"], bool):
                    if filters["is_public"]:
                        query = query.filter(GoalCate.user_id.is_(None))
                    else:
                        query = query.filter(GoalCate.user_id.isnot(None))

            # 分页查询（按 ID 降序，最新分类在前）
            pagination = query.order_by(GoalCate.id.desc()).paginate(
                page=page, per_page=per_page, error_out=False
            )

            # 格式化分页结果
            result_items = []
            for cate, username in pagination.items:
                result_items.append({
                    "id": cate.id,
                    "user_id": cate.user_id,
                    "username": username or "公共分类",
                    "first_cate_name": cate.first_cate_name,
                    "create_time": cate.create_time.strftime("%Y-%m-%d %H:%M:%S") if hasattr(cate,
                                                                                             'create_time') else None
                })

            # 返回分页信息
            return {
                "items": result_items,
                "total": pagination.total,
                "page": page,
                "per_page": per_page,
                "pages": pagination.pages
            }
        except Exception as e:
            raise Exception(f"查询分类列表失败：{str(e)}")

    @staticmethod
    def get_goal_cate_by_name(cate_name):
        """
        根据分类名称查询分类（用于校验名称唯一性）
        :param cate_name: 分类名称
        :return: 分类对象（不存在则返回 None）
        """
        try:
            return GoalCate.query.filter(GoalCate.first_cate_name == cate_name).first()
        except Exception as e:
            raise Exception(f"查询分类失败：{str(e)}")